home *** CD-ROM | disk | FTP | other *** search
/ NeXT Education Software Sampler 1992 Fall / NeXT Education Software Sampler 1992 Fall.iso / Programming / Classes / SocketClasses / SktSocketManager.m < prev    next >
Encoding:
Text File  |  1992-07-23  |  37.0 KB  |  914 lines

  1. /***************************************************************************
  2. *                                                                          *
  3. * SktSocketManager.m                                                       *
  4. * Copyright 1992 by Nik A Gervae                                           *
  5. *                                                                          *
  6. * One of a set of three Objective-C classes (SktSocketManager, SktSocket,  *
  7. * and SktSocketUser) which implement a convenient interface to Berkeley    *
  8. * stream sockets under NeXTSTEP(r).  See the accompanying class            *
  9. * specifications (files with a .rtf or .spec suffix) for further           *
  10. * information.                                                             *
  11. *                                                                          *
  12. * NeXTSTEP is a registered trademark of NeXT Computer, Inc.                *
  13. *                                                                          *
  14. ****************************************************************************
  15. *                                                                          *
  16. * LICENSE                                                                  *
  17. *                                                                          *
  18. * This program is free software; you can redistribute it and/or modify     *
  19. * it under the terms of the GNU General Public License as published by     *
  20. * the Free Software Foundation.                                            *
  21. *                                                                          *
  22. * The program and this makefile are distributed in the hope that it will   *
  23. * be useful, but are provided "AS IS" AND WITHOUT ANY WARRANTY; without    *
  24. * any express or implied warranty of MERCHANTABILITY or FITNESS FOR A      *
  25. * PARTICULAR PURPOSE. See the GNU General Public License for more details. *
  26. * Any use or distribution of the program and documentation must include    *
  27. * appropriate copyrights to acknowledge Nik A. Gervae and the Free         *
  28. * Software Foundation, Inc.                                                *
  29. * You should have received a copy of the GNU General Public License        *
  30. * along with this program; if not, write to the Free Software              *
  31. * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.                *
  32. *                                                                          *
  33. ****************************************************************************
  34. *                                                                          *
  35. * VERSION HISTORY                                                          *
  36. *                                                                          *
  37. * Version numbers are simply dates in the form YYYYMMDD.  These represent  *
  38. * the date that version was finished.  Only significantly changed versions *
  39. * are reported here, or those versions requiring explanation of changes.   *
  40. * There may be many interim stages between dated versions.                 *
  41. *                                                                          *
  42. * DateVersion Primary Author  Notes                                        *
  43. * ----------- --------------- -------------------------------------------- *
  44. * 19920327    Nik A Gervae    First released version                       *
  45. * 19920723    Nik A Gervae    Actually released                            *
  46. *                                                                          *
  47. ***************************************************************************/
  48.  
  49. #import <errno.h>
  50. #import <stdio.h>
  51.  
  52. #import <sys/param.h>
  53. #import <sys/types.h>
  54. #import <sys/socket.h>
  55. #import <sys/time.h>
  56. #import <netinet/in.h>
  57. #import <netdb.h>
  58.  
  59. #import <objc/List.h>
  60.  
  61. #import "SktSocketManager.h"
  62.  
  63. extern int errno;
  64.  
  65.  
  66. /***************************************************************************
  67. *                                                                          *
  68. * These are the constant strings used.  Feel free to translate them into   *
  69. * your favorite language.  Do be sure to keep all the % directives in      *
  70. * place, or change the code that accesses these strings.                   *
  71. *                                                                          *
  72. ***************************************************************************/
  73. #define STR_Error             "ERROR (%s): "
  74.  
  75. #define STR_MallocFatalError   STR_Error "zone malloc failed.\n"
  76.  
  77. #define STR_BadCapacity       STR_Error "specified capacity (%d) invalid.\n"
  78. #define STR_NoUserClass       STR_Error "no user class specified for " \
  79.                                "initializing.\n"
  80. #define STR_NotUserClass      STR_Error "%s is not a subclass of " \
  81.                                "SktSocketUser.\n"
  82. #define STR_CantGetSocket     STR_Error "unable to get service socket.\n"
  83. #define STR_CantSetOptions    STR_Error "unable to set sender\'s options.\n"
  84. #define STR_CantBindSocket    STR_Error "unable to bind socket.\n"
  85. #define STR_CantGetSocketName STR_Error "unable to get socket name.\n"
  86. #define STR_CantCreateList    STR_Error "unable to create openSockets " \
  87.                                "list.\n"
  88.  
  89.  
  90. #define STR_NoHostInfo        "(%s) Cannot get host information.\n"
  91. #define STR_NoFds             "(%s) No more fds. Closing new connection.\n"
  92. #define STR_NoNewConnects     "No new connections can be accepted  at this " \
  93.                                "time. Sorry.\n"
  94. #define STR_ClosingSocket     "(%s) closing SktSocket with socketFd %d\n"
  95.  
  96. #define STR_SelectEBADF       "(%s) bad fd in select().\n"
  97. #define STR_SelectEINTR       "(%s) signal during select().\n"
  98. #define STR_SelectEINVAL      "(%s) invalid timeout in select().\n"
  99. #define STR_SelectErrUnknown  "(%s) unknown select() error #%d.\n"
  100. #define STR_NewSocketDied     STR_Error "coudn\'t create/init new socket.\n"
  101. #define STR_NewUserDied       STR_Error "coudn\'t create/init new user.\n"
  102. #define STR_CantCreateUserSorry "Can\'t create needed resources to accept " \
  103.                                  "a new connection.  Sorry.\n"
  104.  
  105. #define STR_Unknown           "(unknown)"
  106.  
  107.  
  108.  
  109. @implementation SktSocketManager
  110.  
  111. /***************************************************************************
  112. *                                                                          *
  113. * -init                                                                    *
  114. *                                                                          *
  115. ***************************************************************************/
  116. - init
  117. {
  118.   return [self notImplemented:_cmd];
  119. }
  120.  
  121. /***************************************************************************
  122. *                                                                          *
  123. * -initPort:logfile:fdCapacity:userClass:options:                          *
  124. *                                                                          *
  125. * THIS IS THE DESIGNATED INITIALIZER FOR THIS CLASS.                       *
  126. *                                                                          *
  127. * This method does a hell of a lot.  It has to establish all ivars, check  *
  128. * the amount of available file descriptors for sockets, check the supplied *
  129. * user class, get a socket for service connections, bind it, verify is,    *
  130. * and listen for connections. See below for the nitty-gritty.              *
  131. *                                                                          *
  132. * ERROR CONDITIONS: The following conditions cause a return of nil:        *
  133. *                                                                          *
  134. * - The specified capacity is invalid.                                     *
  135. * - The specified user class is nil or is not a subclass of SktSocketUser. *
  136. * - The socket can't be created or bound, or verified.                     *
  137. * - setSocketOptions returns nil.                                          *
  138. * - The List object of open SktSockets can't be allocated/initted.         *
  139. *                                                                          *
  140. ***************************************************************************/
  141. - initPort:(int)portNum logfile:(FILE *)file fdCapacity:(int)cap
  142.     userClass:aClass
  143. {
  144.   int  slen = sizeof(struct sockaddr_in);  // for use in inet functions
  145.   char host_name[MAXHOSTNAMELEN+1];        // for getting the hostname
  146.   struct sockaddr_in  server;              // for binding the socket
  147.   struct sockaddr_in  hostaddr_in;         // for getting the inet address
  148.   struct hostent     *host;                // for getting the inet address
  149.  
  150.   [super init];
  151.   zone = [self zone];
  152.  
  153.  /*
  154.   * Set up the logfile right away, and initialize ivars to be void
  155.   * until we can fill them in later.  Also check cap against the
  156.   * number of available fds.
  157.   */
  158.   logfile     = file;
  159.   userClass   = nil;
  160.   openSockets = nil;
  161.   hostaddress = hostname = NULL;
  162.   doesLog     = YES;
  163.  
  164.  /*
  165.   * Default select() behavior in -update is to poll (0 timeout),
  166.   * not block, and not to cover any signals.
  167.   */
  168.   selectTimeout.tv_sec  = 0;
  169.   selectTimeout.tv_usec = 0;
  170.   timeoutIndefinite     = NO;
  171.   selectSignalMask      = 0;
  172.  
  173.   if (0 > cap || getdtablesize() < cap) {
  174.     [self log:STR_BadCapacity, [self name], cap];
  175.     return [self free];
  176.   }
  177.   numAvailFds = cap;
  178.  
  179.  /*
  180.   * If aClass is not a subclass of SktSocketUser, don't init.
  181.   */
  182.   if (!aClass) {
  183.     [self log:STR_NoUserClass, [self name]];
  184.     return [self free];
  185.   }
  186.  
  187.   if (![aClass isKindOf:objc_getMetaClass([[SktSocketUser class] name])]) {
  188.     [self log:STR_NotUserClass, [self name], [aClass name]];
  189.     return [self free];
  190.   }
  191.  
  192.   userClass = [aClass class];  // Send class message just in case.
  193.  
  194.  /*
  195.   * A new socket, please.  And don't init if we can't get one.
  196.   * If the sender wanted to set some options, try to do so, but
  197.   * don't init if the provided function returns NO.
  198.   */
  199.   serviceSocketFd = socket(AF_INET, SOCK_STREAM, 0);
  200.   if (0 > serviceSocketFd) {
  201.     [self log:STR_CantGetSocket, [self name]];
  202.     return [self free];
  203.   }
  204.  
  205.   if (![self setSocketOptions:serviceSocketFd]) {
  206.     [self log:STR_CantSetOptions, [self name]];
  207.     return [self free];
  208.   }
  209.  
  210.  /*
  211.   * bind() the socket.  This part is messy, 'cause we have to get
  212.   * all sorts of random garbage info to get what we really need.
  213.   */
  214.   gethostname(host_name, sizeof(host_name));
  215.   if (NULL == (host = gethostbyname(host_name))) {
  216.     server.sin_family = AF_INET;
  217.     hostaddress = NULL;
  218.   }
  219.   else {
  220.     server.sin_family = host->h_addrtype;
  221.     memcpy((char *)&hostaddr_in.sin_addr.s_addr, (char *)host->h_addr_list[0], 
  222.           host->h_length);
  223.  
  224.    /*
  225.     * Not fatal if this fails. (May need to change this.)
  226.     */
  227.     hostaddress = zoneStrdup([self zone],
  228.                              (char *)inet_ntoa(hostaddr_in.sin_addr));
  229.  
  230.     if (!hostaddress) {
  231.       [self log:STR_MallocFatalError, [self name]];
  232.     }
  233.   }
  234.  
  235.  /*
  236.   * Not fatal if this fails.  (May need to change this.)
  237.   */
  238.   hostname = zoneStrdup([self zone], host_name);
  239.   if (!hostname) {
  240.     [self log:STR_MallocFatalError, [self name]];
  241.   }
  242.  
  243.   server.sin_addr.s_addr = htonl(INADDR_ANY);
  244.   server.sin_port        = htonl(portNum);
  245.   if (bind(serviceSocketFd, (struct sockaddr *)&server, slen)) {
  246.     [self log:STR_CantBindSocket, [self name]];
  247.     return [self free];
  248.   }
  249.  
  250.  /*
  251.   * Verify socket and listen() for connection requests.
  252.   */
  253.   if (0 > getsockname(serviceSocketFd, (struct sockaddr *)&server, &slen)) {
  254.     [self log:STR_CantGetSocketName, [self name]];
  255.     return [self free];
  256.   }
  257.   listen(serviceSocketFd, 5);
  258.  
  259.  /*
  260.   * Establish more instance variables.
  261.   */
  262.   servicePort = portNum;
  263.   maxSocketFd = serviceSocketFd;
  264.  
  265.   openSockets = [[List allocFromZone:[self zone]] initCount:5];
  266.  
  267.   if (!openSockets) {
  268.     [self log:STR_CantCreateList, [self name]];
  269.     return [self free];
  270.   }
  271.  
  272.   return self;
  273.  
  274. } /*initPort:logfile:fdCapacity:userClass:*/
  275.  
  276. /***************************************************************************
  277. *                                                                          *
  278. * -setSocketOptions                                                        *
  279. *                                                                          *
  280. * This method sets no options, as the SktSocketManager needs none set.  It *
  281. * may be overriden in subclasses to set other socket/file descriptor       *
  282. * options via setsockopt/fcntl.                                            *
  283. *                                                                          *
  284. * If this method returns nil, then any initialization is aborted and the   *
  285. * init... method will return nil.                                          *
  286. *                                                                          *
  287. ***************************************************************************/
  288. - setSocketOptions:(int)fd
  289. {
  290.   return self;
  291. }
  292.  
  293. /***************************************************************************
  294. *                                                                          *
  295. * -free                                                                    *
  296. *                                                                          *
  297. * Does the obvious.  Also kills all connections.                           *
  298. *                                                                          *
  299. ***************************************************************************/
  300. - free
  301. {
  302.   if (hostaddress) free(hostaddress);
  303.   if (hostname) free(hostname);
  304.   [self closeAllSockets];
  305.   if (openSockets) [openSockets free];
  306.   shutdown(serviceSocketFd, 2);
  307.   return [super free];
  308. }
  309.  
  310. /***************************************************************************
  311. *                                                                          *
  312. * -setDoesLog:                                                             *
  313. *                                                                          *
  314. * Allows you to change whether the user logs non-error messages (which may *
  315. * nonetheless be quite important). The default is YES.                     *
  316. *                                                                          *
  317. ***************************************************************************/
  318. - setDoesLog:(BOOL)flag
  319. {
  320.   doesLog = flag;
  321.   return self;
  322. }
  323.  
  324. /***************************************************************************
  325. *                                                                          *
  326. * -doesLog                                                                 *
  327. *                                                                          *
  328. * YES if non-error messages are logged (the default), NO if not.  Simple.  *
  329. *                                                                          *
  330. ***************************************************************************/
  331. - (BOOL)doesLog
  332. {
  333.   return doesLog;
  334. }
  335.  
  336. /***************************************************************************
  337. *                                                                          *
  338. * -setTimeoutSeconds:microseconds:                                         *
  339. *                                                                          *
  340. * Sets the timeout for checking i/o.  The SktSocketManager will not wait   *
  341. * longer than this before beginning to process an -update message.  If the *
  342. * timeout is (0,0) then -update simply polls for immediately available i/o *
  343. * operations and performs them.                                            *
  344. *                                                                          *
  345. * This method clears and deactivates timeoutIndefinite.                    *
  346. *                                                                          *
  347. ***************************************************************************/
  348. - setTimeoutSeconds:(long int)secs microseconds:(long int)usecs
  349. {
  350.   if (0 > secs || 0 > usecs) return nil;
  351.  
  352.   timeoutIndefinite     = NO;
  353.   selectTimeout.tv_sec  = secs;
  354.   selectTimeout.tv_usec = usecs;
  355.  
  356.   return self;
  357. }
  358.  
  359. /***************************************************************************
  360. *                                                                          *
  361. * -getTimeoutSeconds:microseconds:                                         *
  362. *                                                                          *
  363. * Gets the timeout for checking i/o.  The SktSocketManager will not wait   *
  364. * longer than this before beginning to process an -update message.  If the *
  365. * timeout is (0,0) then -update simply polls for immediately available i/o *
  366. * operations and performs them.                                            *
  367. *                                                                          *
  368. * This method returns NO if the timeout does not apply; that is, if        *
  369. * timeoutIndefinite is in effect.                                          *
  370. *                                                                          *
  371. ***************************************************************************/
  372. - (BOOL)getTimeoutSeconds:(long int *)secs microseconds:(long int *)usecs
  373. {
  374.   *secs  = selectTimeout.tv_sec;
  375.   *usecs = selectTimeout.tv_usec;
  376.  
  377.   return !timeoutIndefinite;
  378. }
  379.  
  380. /***************************************************************************
  381. *                                                                          *
  382. * -setTimeoutIndefinite                                                    *
  383. *                                                                          *
  384. * Causes the SktSocketManager to block indefinitely in -update until there *
  385. * is an i/o operation to perform, or a signal occurs.                      *
  386. *                                                                          *
  387. ***************************************************************************/
  388. - setTimeoutIndefinite
  389. {
  390.   timeoutIndefinite = YES;
  391.   return self;
  392. }
  393.  
  394. /***************************************************************************
  395. *                                                                          *
  396. * -isTimeoutIndefinite                                                     *
  397. *                                                                          *
  398. * Returns whether the SktSocketManager blocks indefinitely in -update      *
  399. * until there is an i/o operation to perform, or a signal occurs.          *
  400. *                                                                          *
  401. ***************************************************************************/
  402. - (BOOL)isTimeoutIndefinite
  403. {
  404.   return timeoutIndefinite;
  405. }
  406.  
  407. /***************************************************************************
  408. *                                                                          *
  409. * -setSignalMask:                                                          *
  410. *                                                                          *
  411. * Sets the signal mask used during a select(), and returns the old one.    *
  412. * Note the difference from the standard sigsetmask() system call!          *
  413. *                                                                          *
  414. ***************************************************************************/
  415. - (int)setSignalMask:(int)sigmask;
  416. {
  417.   int oldmask = selectSignalMask;
  418.   selectSignalMask = sigmask;
  419.   return oldmask;
  420. }
  421.  
  422. /***************************************************************************
  423. *                                                                          *
  424. * -signalMask                                                              *
  425. *                                                                          *
  426. * Returns the signal mask used during a select().  This is not a cover for *
  427. * the sigmask() system call!                                               *
  428. *                                                                          *
  429. ***************************************************************************/
  430. - (int)signalMask;
  431. {
  432.   return selectSignalMask;
  433. }
  434.  
  435. /***************************************************************************
  436. *                                                                          *
  437. * -setFdCapacity:                                                          *
  438. *                                                                          *
  439. * Sets the maximum amount of connections that will be allowed at one time. *
  440. * Various error checking is done.  This method can be used to reserve some *
  441. * of the process's file descriptors for other use (log/temp files).        *
  442. *                                                                          *
  443. ***************************************************************************/
  444. - setFdCapacity:(int)cap
  445. {
  446.   int assigned;
  447.  
  448.   assigned = [openSockets count];
  449.  
  450.   if (getdtablesize() < cap || assigned > cap) {
  451.     return nil;
  452.   }
  453.   else if (0 > cap) {
  454.     numAvailFds = 0;
  455.     return nil;
  456.   }
  457.   else {
  458.     numAvailFds = cap - assigned;
  459.   }
  460.   return self;
  461. }
  462.  
  463. /***************************************************************************
  464. *                                                                          *
  465. * -adjustFdCapacity:                                                       *
  466. *                                                                          *
  467. * Adjusts the maximum amount of connections that will be allowed at one    *
  468. * time.  Various error checking is done.  This method can be used to       *
  469. *  reserve some of the process's file descriptors for other use (log/temp  *
  470. * files).                                                                  *
  471. *                                                                          *
  472. ***************************************************************************/
  473. - (int)adjustFdCapacity:(int)byAmount
  474. {
  475.   int newAvailFds;
  476.   int assigned;
  477.  
  478.   newAvailFds = numAvailFds + byAmount;
  479.   assigned = [openSockets count];
  480.  
  481.   if (getdtablesize() < newAvailFds + assigned) {
  482.  
  483.     return -1;
  484.  
  485.   } else if (0 > newAvailFds) {
  486.  
  487.     numAvailFds = 0;
  488.     return -1;
  489.  
  490.   }
  491.   else {
  492.     numAvailFds = newAvailFds;
  493.   }
  494.   return numAvailFds + assigned;
  495. }
  496.  
  497. /***************************************************************************
  498. *                                                                          *
  499. * -fdCapacity                                                              *
  500. *                                                                          *
  501. * Returns the maximum amount of connections that will be allowed at one    *
  502. * time.                                                                    *
  503. *                                                                          *
  504. ***************************************************************************/
  505. - (int)fdCapacity
  506. {
  507.   return numAvailFds + [openSockets count];
  508. }
  509.  
  510. /***************************************************************************
  511. *                                                                          *
  512. * -numAvailFds                                                             *
  513. *                                                                          *
  514. * Returns the amount of file desctiptors free for further connections.     *
  515. *                                                                          *
  516. ***************************************************************************/
  517. - (int)numAvailFds
  518. {
  519.   return numAvailFds;
  520. }
  521.  
  522. /***************************************************************************
  523. *                                                                          *
  524. * -logfile                                                                 *
  525. *                                                                          *
  526. * Return the FILE pointer for the file all log messages are written to.    *
  527. *                                                                          *
  528. ***************************************************************************/
  529. - (FILE *)logfile
  530. {
  531.   return logfile;
  532. }
  533.  
  534. /***************************************************************************
  535. *                                                                          *
  536. * -hostaddress                                                             *
  537. *                                                                          *
  538. * Returns the address of the host the SktSocketManager's process is        *
  539. * on, in dot notation.                                                     *
  540. *                                                                          *
  541. ***************************************************************************/
  542. - (const char *)hostaddress
  543. {
  544.   if (hostaddress) return (const char *)hostaddress;
  545.   else return STR_Unknown;
  546. }
  547.  
  548. /***************************************************************************
  549. *                                                                          *
  550. * -hostname                                                                *
  551. *                                                                          *
  552. * Returns the name of the host the SktSocketManager's process is on.       *
  553. *                                                                          *
  554. ***************************************************************************/
  555. - (const char *)hostname
  556. {
  557.   if (hostname) return (const char *)hostname;
  558.   else return STR_Unknown;
  559. }
  560.  
  561. /***************************************************************************
  562. *                                                                          *
  563. * -servicePort                                                             *
  564. *                                                                          *
  565. * The Internet port number used to connect to the SktSocketManager.        *
  566. *                                                                          *
  567. ***************************************************************************/
  568. - (int)servicePort
  569. {
  570.   return servicePort;
  571.  
  572. /***************************************************************************
  573. *                                                                          *
  574. * -getInetAddresses                                                        *
  575. *                                                                          *
  576. * Return a NULL-terminated array of character strings representing all of  *
  577. * the Internet addresses of the host machine. The sender may free the      *
  578. * array returned when it is no longer needed (just free the top-level      *
  579. * pointer).                                                                *
  580. *                                                                          *
  581. * The array is allocated in the default malloc zone.                       *
  582. *                                                                          *
  583. * ERROR CONDITION: If the allocation of the array fails, a message is      *
  584. * logged, and NULL is returned.                                            *
  585. *                                                                          *
  586. ***************************************************************************/
  587. - (char **)getInetAddresses
  588. {
  589.   struct hostent     *host;          // the record for the host
  590.   struct sockaddr_in  thisHostaddr;  // 
  591.   int                 count, this;
  592.   int                 totalSize;
  593.   char              **addresses; // Used to return inet addresses
  594.   char               *loc;
  595.  
  596.  /*
  597.   * Get the host's name and count the number of addresses it has.
  598.   */
  599.   if (NULL == (host = gethostbyname(hostname))) {
  600.     [self log:STR_NoHostInfo, [self name]];
  601.     return NULL;
  602.   }
  603.   for (count = 0; host->h_addr_list[count] != NULL; count++)
  604.     ;
  605.  
  606.  /*
  607.   * Count up the total amount we need to malloc to get all the
  608.   * strings and their pointers.
  609.   */
  610.   totalSize = (1+count) * sizeof(char *);
  611.  
  612.   for (this = 0; this < count && NULL != host->h_addr_list[this]; this++) {
  613.     memcpy((char *)&(thisHostaddr.sin_addr.s_addr),
  614.            (char *)host->h_addr_list[this],
  615.            host->h_length);
  616.  
  617.     totalSize += 1 + strlen(inet_ntoa(thisHostaddr.sin_addr));
  618.   }
  619.   addresses = (char **)NXZoneMalloc(NXDefaultMallocZone(), totalSize);
  620.   if (!addresses) {
  621.     [self log:STR_MallocFatalError, [self name]];
  622.     return NULL;
  623.   }
  624.  
  625.  /*
  626.   * Now copy each address into the proper offset in the array, and
  627.   * set a pointer to it at the beginning.
  628.   */
  629.   loc = (char *)(addresses + count + 1);
  630.   for (this = 0; this < count && NULL != host->h_addr_list[this]; this++) {
  631.  
  632.     char     *dummy;
  633.     long int  size;
  634.  
  635.     memcpy((char *)&(thisHostaddr.sin_addr.s_addr),
  636.            (char *)host->h_addr_list[this],
  637.            host->h_length);
  638.     dummy = (char *)inet_ntoa(thisHostaddr.sin_addr);
  639.     size = 1+strlen(dummy);
  640.  
  641.     *(addresses + this) = loc;
  642.     memcpy(loc, dummy, size);
  643.     loc += size;
  644.   }
  645.  
  646.  /*
  647.   * Remember, it's a NULL-terminated array of arrays.
  648.   */
  649.   addresses[this] = NULL;
  650.  
  651.   return addresses;
  652.  
  653. } /*getInetAddresses*/
  654.  
  655. /***************************************************************************
  656. *                                                                          *
  657. * -update                                                                  *
  658. *                                                                          *
  659. * In brief (see below for details):                                        *
  660. *  - Do asynchronous input via select(). This is the messy part.           *
  661. *  - Drop any sockets with exceptions pending.                             *
  662. *  - Have individual sockets flush output.                                 *
  663. *  - Have them queue input.                                                *
  664. *  - Check for new connections. (Always do this last.)                     *
  665. *                                                                          *
  666. * ERROR CONDITION: If a new SktSocket/User pair need to be created, and    *
  667. * either can't be, this method logs a message and returns immediately.     *
  668. * This is not a fatal error.                                               *
  669. *                                                                          *
  670. ***************************************************************************/
  671. - update
  672. {
  673.   int             i;                // general loop variable
  674.   id              currentSocket;    // use in loops
  675.   int             currentSocketFd;  // use in loops
  676.  
  677.   int             oldmask;          // used to protect select() from signals
  678.   int             numpending;       // result of select()
  679.   fd_set          readfds;          // for select()
  680.   fd_set          exceptfds;        // for select()
  681.   struct timeval *timeout;          // for select()
  682.  
  683.  /*
  684.   * Prepare for select().  Set the timeout, the fd sets, and
  685.   * mask any signals requested.
  686.   */
  687.   if (timeoutIndefinite) timeout = NULL;
  688.   else timeout = &selectTimeout;
  689.  
  690.   FD_ZERO(&readfds);
  691.   FD_ZERO(&exceptfds);
  692.  
  693.   FD_SET(serviceSocketFd, &readfds);
  694.   for (i = [openSockets count]-1; 0 <= i; i--) {
  695.     currentSocketFd = [[openSockets objectAt:i] socketFd];
  696.     FD_SET(currentSocketFd, &readfds);
  697.     FD_SET(currentSocketFd, &exceptfds);
  698.   }
  699.  
  700.   oldmask = sigsetmask(selectSignalMask);
  701.  
  702.   numpending = select(maxSocketFd+1,
  703.                       &readfds,
  704.                       (fd_set *)0,  // SktSockets handle write()s
  705.                       &exceptfds,
  706.                       timeout);
  707.   sigsetmask(oldmask);
  708.  
  709.  /*
  710.   * Error occured: report and don't do anything this time
  711.   * (odds are it was a signal).
  712.   */
  713.   if (0 > numpending) {
  714.     if (doesLog) switch (errno) {
  715.       case EBADF:
  716.         [self log:STR_SelectEBADF, [self name]];
  717.         break;
  718.       case EINTR:
  719.         [self log:STR_SelectEINTR, [self name]];
  720.         break;
  721.       case EINVAL:
  722.         [self log:STR_SelectEINVAL, [self name]];
  723.         break;
  724.       default:
  725.         [self log:STR_SelectErrUnknown, [self name], errno];
  726.         break;
  727.     }
  728.     return self;
  729.   }
  730.  
  731.  /*
  732.   * Drop sockets with exception conditions (assume corrupt connection).
  733.   * It is the socket user's responsibility to notice that its SktSocket
  734.   * is gone, and to clean up appropriately.
  735.  
  736.   * Have to go backwards 'cause List slides contents forward on removal.
  737.   */
  738.   for (i = [openSockets count]-1; 0 <= i; i--) {
  739.  
  740.     currentSocket = [openSockets objectAt:i];
  741.     currentSocketFd = (int)[currentSocket socketFd];
  742.  
  743.     if (FD_ISSET(currentSocketFd, &exceptfds)) {
  744.       FD_CLR(currentSocketFd, &readfds);
  745.       [self closeSocket:currentSocket];
  746.     }
  747.  
  748.   }
  749.  
  750.  /*
  751.   * Have all sockets flush output. We don't need writefds for this.
  752.   */
  753.   [openSockets makeObjectsPerform:@selector(flushOutput)];
  754.  
  755.  /*
  756.   * Have each socket read one block of input. Use the info from
  757.   * select() in this messy loop to avoid the hassle and overhead
  758.   * of nonblocking read()'s where they might be unnecessary.
  759.   * If readInput returns nil, the socket connection is closed/dead
  760.   * (EOF), and the SktSocket should be closed.  It is the socket
  761.   * user's responsibility to notice that its SktSocket is gone,
  762.   * and to clean up appropriately.
  763.   */
  764.   for (i = [openSockets count]-1; 0 <= i; i--) {
  765.  
  766.     currentSocket   = [openSockets objectAt:i];
  767.     currentSocketFd = [currentSocket socketFd];
  768.  
  769.     if (FD_ISSET(currentSocketFd, &readfds)) {      
  770.       if (![currentSocket readInput]) {
  771.         [self closeSocket:currentSocket];
  772.       }
  773.     }
  774.   }
  775.  
  776.  /* 
  777.   * Accept new connections if possible.
  778.   */
  779.   if (FD_ISSET(serviceSocketFd, &readfds)) {
  780.  
  781.     id  newSocket;
  782.     int newSocketFd;
  783.     id  newUser;
  784.  
  785.    /*
  786.     * Create a new SktSocket and a user.  Log messages if they
  787.     * can't be created, and return.
  788.     */
  789.     newSocket = [[SktSocket allocFromZone:zone] initOnFd:serviceSocketFd
  790.                  withManager:self];
  791.     if (!newSocket) {
  792.       [self log:STR_NewSocketDied, [self name]];
  793.       return self;
  794.     }
  795.  
  796.     newSocketFd = [newSocket socketFd];
  797.  
  798.     newUser = [[userClass allocFromZone:zone] initWithSocket:newSocket];
  799.     if (!newUser) {
  800.       [self log:STR_NewUserDied, [self name]];
  801.       [newSocket queueOutputString:STR_CantCreateUserSorry];
  802.       [newSocket flushOutput];
  803.       [newSocket free];
  804.       return self;
  805.     }
  806.  
  807.    /*
  808.     * Check that we have room for it and update bookkeeping info.
  809.     */
  810.     if ([openSockets count] < numAvailFds) {
  811.       if (newSocketFd > maxSocketFd) maxSocketFd = newSocketFd;
  812.       [openSockets addObject:newSocket];
  813.     }
  814.     else {
  815.       [self log:STR_NoFds, [self name]];
  816.       [newSocket queueOutputString:STR_NoNewConnects];
  817.       [newSocket flushOutput];
  818.  
  819.       [newSocket free];
  820.       [newUser free];
  821.     } 
  822.  
  823.   } /*if (FD_ISSET())*/
  824.  
  825.   return self;
  826.  
  827. } /*update*/
  828.  
  829. /***************************************************************************
  830. *                                                                          *
  831. * -closeSocket:                                                            *
  832. *                                                                          *
  833. * Remove a socket from the list of currently open sockets, have the        *
  834. * SktSocket object clean itself up and die, and update bookeeping info.    *
  835. * Do NOT kill the socket user here!  It has to find out its socket is gone *
  836. * and take appropriate action.                                             *
  837. *                                                                          *
  838. ***************************************************************************/
  839. - closeSocket:socketObj
  840. {
  841.   [self log:STR_ClosingSocket, [self name], [socketObj socketFd]];
  842.  
  843.   [openSockets removeObject:socketObj];
  844.   if ([socketObj socketFd] == maxSocketFd) {
  845.     maxSocketFd--;
  846.   }
  847.  
  848.   [socketObj free];
  849.  
  850.   return self;
  851.  
  852. }
  853.  
  854. /***************************************************************************
  855. *                                                                          *
  856. * closeAllSockets                                                          *
  857. *                                                                          *
  858. * Have all SktSocket objects clean up and die.                             *
  859. *                                                                          *
  860. ***************************************************************************/
  861. - closeAllSockets
  862. {
  863.   int i;
  864.  
  865.   for (i = [openSockets count] - 1; 0 <= i; i--) {
  866.     [self closeSocket:[openSockets objectAt:i]];
  867.   }
  868.   return self;
  869.  
  870. }
  871.  
  872. /***************************************************************************
  873. *                                                                          *
  874. * -announceString:                                                         *
  875. *                                                                          *
  876. * Dump the given string on each socket.                                    *
  877. *                                                                          *
  878. ***************************************************************************/
  879. - announceString:(const char *)announcement
  880. {
  881.   int numfds;
  882.   int i;
  883.  
  884.   numfds = [openSockets count];
  885.   for (i = 0; i < numfds; i++) {
  886.     [[openSockets objectAt:i] queueOutputString:announcement];
  887.   }
  888.  
  889.   return self;
  890. }
  891.  
  892. /***************************************************************************
  893. *                                                                          *
  894. * -log:                                                                    *
  895. *                                                                          *
  896. * Print a message to the logfile.  You can use this exactly like printf,   *
  897. * 'cause that's what it uses.                                              *
  898. *                                                                          *
  899. ***************************************************************************/
  900. - log:(const char *)format, ...
  901. {
  902.   va_list  param_list;
  903.  
  904.   va_start(param_list, format);
  905.   vfprintf(logfile, format, param_list);
  906.   fflush(logfile);
  907.   va_end(param_list);
  908.   return self;
  909.  
  910. }
  911.  
  912. @end /*implementation SktSocketManager*/
  913.